
package atm.components;

import java.util.ArrayList;
import java.util.List;

import atm.model.Tray;

/**
 * Cash dispenser is responsible for ejecting money from ATM.
 */
public class CashDispenser {

	private CashDispenserDb db;

	public CashDispenser() {
		setDb(new CashDispenserDb());
	}

	/**
	 * Ejects amount from ATM.
	 * 
	 * We assume that money will always be ejected if there is enough of it in the ATM
	 * 
	 * @param amount
	 *            Amount to eject
	 */
	public void ejectAmount(int amount) {
		if (getDb() == null)
			throw new IllegalStateException("Reference to database is null");
		if (getDb().getTrays() == null)
			throw new IllegalStateException("Reference to trays is null");
		if (getDb().getTrays().getTrays() == null)
			throw new IllegalStateException("Reference to trays is null");
		if (getDb().getTrays().getTrays().size() <= 0)
			throw new IllegalStateException("Tray size is 0 or smaller");
		if (!isEnoughCash(amount))
			throw new IllegalStateException("Not enough cash in the ATM");

		int amountLeft = amount;
		List<Tray> trays = getDb().availableBills();
		List<Integer> usedBills = new ArrayList<Integer>();

		for (Tray tray : trays) {
			while (tray.getBillCount() > 0 && amountLeft > tray.getBillDenomination()) {
				amountLeft -= tray.getBillDenomination();
				usedBills.add(tray.getBillDenomination());
			}
		}

		getDb().subtractBills(usedBills);

	}

	public CashDispenserDb getDb() {
		return db;
	}

	private List<Tray> getWorkingCopy(List<Tray> trays) {
		if (trays == null)
			throw new IllegalStateException("No trays found");
		List<Tray> tempList = new ArrayList<Tray>();

		for (Tray tray : trays) {
			Tray tempTray = new Tray();
			tempTray.setBillCount(tray.getBillCount());
			tempTray.setBillDenomination(tray.getBillDenomination());
			tempList.add(tempTray);
		}

		return tempList;

	}

	/**
	 * Checks if cash dispenser contains enough bills.
	 * 
	 * @param amount
	 *            Amount to check
	 * @return true if there are enough bills
	 */

	public boolean isEnoughCash(int amount) {
		if (getDb() == null)
			throw new IllegalStateException("Reference to database is null");
		if (getDb().getTrays() == null)
			throw new IllegalStateException("Reference to trays is null");
		if (getDb().getTrays().getTrays() == null)
			throw new IllegalStateException("Reference to trays is null");
		if (getDb().getTrays().getTrays().size() <= 0)
			throw new IllegalStateException("Tray size is 0 or smaller");
		if (amount <= 0)
			throw new IllegalStateException("Amount is 0 or less");

		int amountLeft = amount;
		List<Tray> trays = getDb().availableBills();

		trays = getWorkingCopy(trays);

		for (Tray tray : trays) {
			while (tray.getBillCount() > 0 && amountLeft >= tray.getBillDenomination()) {
				amountLeft -= tray.getBillDenomination();
				tray.setBillCount(tray.getBillCount() - 1);
			}
		}

		return amountLeft == 0;

	}

	private void setDb(CashDispenserDb value) {
		db = value;
	}
}
